<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Document</title>
	</head>

	<body>
		<h1>Web Audio</h1>
		<!-- <audio src="./voice.mp3" controls></audio> -->
		<canvas class="cvs"></canvas>
		<div>
			<button
				onclick="play('https://s1.aigei.com/pvaud/aud/mp3/41/412db86f5a4a4eda9c4283690c12a3b2.mp3?e=1647275280&token=P7S2Xpzfz11vAkASLTkfHN7Fw-oOZBecqeJaxypL:t-qsHLTo8dgc_VwfTuZ0v4oTR1A=')"
			>
				音频1
			</button>
			<button
				onclick="play('https://s1.aigei.com/pvaud/aud/mp3/db/db897c9cbe844e85a3ed80c3dbd82b3d.mp3?e=1647275520&token=P7S2Xpzfz11vAkASLTkfHN7Fw-oOZBecqeJaxypL:tr-61LY1PL0w8ZJi7vLu_Zr0BuU=')"
			>
				音频2
			</button>
			<button onclick="play('http://m8.music.126.net/20220314152722/5195b9f8647580fd19f43af9c1dfda55/ymusic/f30b/4d7d/ec35/31b57365c1af727ede0563357ef52c61.mp3')">
				Disco Yes!!
			</button>

			<div>
				<span>音量调节</span>
				<input type="range" min="0" max="5" step="0.1" value="0" oninput="changeVoice(value)" />
			</div>
			<div>
				<span>播发进度</span>
				<button onclick="middle()">中间部分</button>
				<input id="playRange" type="range" value="0" min="0" max="100" />
			</div>
		</div>
	</body>
	<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

	<script>
		const cvs = document.querySelector('canvas')
		const canvasCtx = cvs.getContext('2d')
		const WIDTH = window.innerWidth
		const HEIGHT = 500
		cvs.height = HEIGHT
		cvs.width = WIDTH
		/*
			整体步骤：
			ajax => 原始数据(ArrayBuffer) => 解码后数据(AudioBuffer) => AudioBufferSourceNode(把解码后的数据挂载到音源上) => 通过audioContext.destination交由硬件播放
		*/
		let duration = 0
		let currentPercent = 0
		/*
			audioCtx 音频上下文
			bufferTypeSource buffer音源
			gainNode 处理节点
		*/
		let audioCtx, bufferTypeSource, gainNode, analyserNode, dataArray, audioData
		function play(url) {
			audioCtx && audioCtx.state === 'running' && audioCtx.close()
			audioCtx = new (window.AudioContext || window.webkitAudioContext)()
			bufferTypeSource = audioCtx.createBufferSource()
			analyserNode = audioCtx.createAnalyser()
			gainNode = audioCtx.createGain()
			analyserNode.fftSize = 4096
			axios.get(url, { responseType: 'arraybuffer' }).then((res) => {
				const bufferData = res.data
				audioCtx.decodeAudioData(bufferData).then((audioData) => {
					audioData = audioData
					gainNode.gain.value = 1
					duration = audioData.duration
					bufferTypeSource.buffer = audioData
					bufferTypeSource.connect(gainNode)
					gainNode.connect(analyserNode)
					analyserNode.connect(audioCtx.destination)
					bufferTypeSource.start()
					doDraw()
				})
			})
		}
		function changeVoice(e) {
			gainNode.gain.value = e
		}
		function middle() {}
		function doDraw() {
			let x = 0
			/*
				getByteFrequencyData()方法将当前频率数据复制到传入的Uint8Array（无符号字节数组）中。 
				Uint8Array 的长度应该和 frequencyBinCount 相等
			*/
			//bufferLength 和快速傅里叶变换 (FFT)的精度有关！精度越大，这个buffer越大！
			const bufferLength = analyserNode.frequencyBinCount
			console.log(bufferLength)
			dataArray = new Uint8Array(bufferLength)
			canvasCtx.clearRect(0, 0, WIDTH, HEIGHT) //每次循环都清除一次画布
			analyserNode.getByteFrequencyData(dataArray) // 调用
			for (var i = 0; i < bufferLength; i++) {
				//超出可画的长度就不用画了，节省资源！
				if (x > WIDTH) {
					break
				}
				let barHeight = dataArray[i]
				canvasCtx.fillStyle = 'rgb(' + (barHeight + 100) + ',150,50)'
				canvasCtx.fillRect(x, HEIGHT - barHeight / 2, 5, barHeight / 2)
				x += 5 + 3
			}
			//播放时间超出就停止循环，并且关闭audioCtx！
			if (audioCtx.currentTime >= duration) {
				audioCtx.close()
				return
			} else {
				playRange.value = (audioCtx.currentTime / duration) * 100
			}
			//限制循环频率
			let timer = setTimeout(() => {
				requestAnimationFrame(doDraw)
				clearTimeout(timer)
				timer = null
			}, 6)
		}
	</script>
</html>
